#include "iceberg.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sys/types.h>
#include <signal.h>
#include <cstring>
#include <sstream>
#include <sys/wait.h>

using namespace std;

volatile bool waitingForChild = true;
int childrenCount = -1;
int *pids;

void Poke ( int signal );
void InitPidArray ( void );
void AddPid ( int pid, int index );
void DeletePid ( int pid );
void KillChildren ( void );
bool PidExists ( int pid ); 
void Deletepid ( int pid );

int main ( int argc, char* argv[] )
{
	int offset = 0, rounds;
	int  pid = 0, previousPid = 0;
	ifstream configurationFile;
	fstream inputFile;
	ofstream outputFile;
	char inputName[24], outputName[24], configName[24];

        if ( argc != 8 )
        {
                cerr << "Invalid arguments\n";
                exit ( 1 );
        }

	for ( int i = 0; i < argc; i++ )
	{
		if ( strcmp ( argv[i], "-i" ) == 0 )
		{
			strcpy ( inputName, argv[i + 1] );
			i++;
		}
		else if ( strcmp ( argv[i], "-o" ) == 0 )
		{
			strcpy ( outputName, argv[i + 1] );
			i++;
		}
		else if ( strcmp ( argv[i], "-c" ) == 0 )
		{
			strcpy ( configName, argv[i + 1] );
			i++;
		}
	}

	inputFile.open ( inputName, ios::in | ios::out );
	outputFile.open ( outputName, ios::out );
	configurationFile.open ( configName, ios::in );

	/* ....... ... handler ... .. ........ . parent ... . 
	 *......... ..... ..............
	 */

	signal ( SIGUSR1, Poke );

	/* ........ ... ....... ... ....... ... .. ....... ......	 *workers .. ....... . proccess.
	 */

	while ( configurationFile.eof() == false )
	{
		char buffer[128];
		configurationFile.getline ( buffer, 128 );
		childrenCount++;
	}

	/* .......... ... ..... ... .. ...... .. pids. */

	inputFile.seekg ( 0, ios::end );
	int lenght = inputFile.tellg();

	if ( lenght % 8 != 0 )
	{
		for ( int i = 0; i < 8 % lenght; i++ )
		{
			inputFile << " ";
		}	
	}

	inputFile.close();

	for ( int i = 0; i < lenght; i++)
		outputFile << ' ';	
	
	outputFile.close();

	InitPidArray();

	configurationFile.clear();
	configurationFile.seekg ( 0, ios::beg );

	configurationFile >> rounds;

	for ( int i = 0; i < childrenCount; i++ )
	{
		string hexKey, lenght;
		configurationFile >> hexKey;
		configurationFile >> lenght;

		ostringstream offsetStr, previousPidStr, roundsStr;
		offsetStr << offset;
		previousPidStr << previousPid;
		roundsStr << rounds;

		pid = fork();

		if ( pid == 0 )
		{
			if ( strcmp ( argv[1], "-e" ) == 0 )
			{
				execl ( "./encrypt", "encrypt", inputName, outputName, offsetStr.str().c_str(), hexKey.c_str(), lenght.c_str(), 
previousPidStr.str().c_str(), roundsStr.str().c_str(), NULL );			
				KillChildren();
				
				cout << "exec failed";
				exit ( 1 );
			}
			else if ( strcmp ( argv[1], "-d" ) == 0 )
			{
				execl ( "./decrypt", "decrypt", inputName, outputName, offsetStr.str().c_str(), hexKey.c_str(), lenght.c_str(), 
previousPidStr.str().c_str(), roundsStr.str().c_str(), NULL );			
		                KillChildren();
                                
				cout << "exec failed";
                                exit ( 1 );
			}
			else
			{
				cerr << "Invalid first argument\n";
				exit ( 1 );
			}
		}
		else
		{
			AddPid ( pid, i );
			previousPid = pid;
			
			while ( waitingForChild ){}

			waitingForChild = true;

			offset += atoi ( lenght.c_str() );
		}
	}

	/* ........ ....... ... ......... ..... ... .. .........
	 *. .......... ... (...).............. ... .. .......
	 */

	kill ( pid, SIGUSR1 );

	/* ....... ..... .. .......... .. .......... .......
	 *.. ......... ... ...... .......... .. ....., .............
	 *.... .. .......... .......
	 */

	while ( childrenCount )
	{
		int status;

		pid = wait ( &status );
	
		if ( PidExists ( pid ) == true )
		{
			DeletePid ( pid );
		}
		else
		{
			KillChildren();

			cerr << "Child exited abnomarly\n";
                        exit ( 1 );
		}
	}

	exit ( 0 );
}

void Poke ( int signal )
{
	waitingForChild = false;
}

void InitPidArray ( void )
{
	pids = new int[childrenCount];

}

void AddPid ( int pid, int index )
{
	pids[index] = pid;
}

void KillChildren ( void )
{
	for ( int i = 0; i < childrenCount; i++ )
	{
		kill ( pids[i], SIGKILL );
	}
	
	childrenCount = 0;
}

bool PidExists ( int pid )
{
	for ( int i = 0; i < childrenCount; i++ )
	{
		if ( pids[i] == pid )
		{
			return true;
		}
	}

	return false;
}

void DeletePid ( int pid )
{
	for ( int i = 0; i < childrenCount; i++ )
	{
		if ( pids[i] == pid )
		{
			childrenCount--;
			pids[i] = pids[childrenCount];
			
			return;
		}
	}
}
